package jmine.tec.aom.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jmine.tec.aom.domain.AbstractPropertyValue;
import jmine.tec.aom.domain.Propertized;

/**
 * Helps merging collections of {@link AbstractPropertyValue}.
 * <p>
 * This implementation is not thread-safe.
 * 
 * @author jrenaut
 */
public class PropertyValueMerger {

    private List<Collection<? extends AbstractPropertyValue<?>>> valuesToBeMerged =
            new ArrayList<Collection<? extends AbstractPropertyValue<?>>>();

    /**
     * @param values the values to be merged
     * @return the {@link PropertyValueMerger}
     */
    public PropertyValueMerger mergePropertyValues(Collection<? extends AbstractPropertyValue<?>> values) {
        this.valuesToBeMerged.add(values);
        return this;
    }

    /**
     * @param propertized a {@link Propertized} to be merged
     * @return the {@link PropertyValueMerger}
     */
    public PropertyValueMerger mergePropertyValues(Propertized propertized) {
        return this.mergePropertyValues(propertized.getPropertyValues());
    }

    /**
     * @return the merged list of {@link AbstractPropertyValue}
     */
    public Collection<? extends AbstractPropertyValue<?>> getPropertyValuesCollection() {
        return this.getPropertyValues().values();
    }

    /**
     * @return a map from properties' mnemonic to the corresponding merged {@link AbstractPropertyValue}.
     */
    public Map<String, AbstractPropertyValue<?>> getPropertyValues() {
        Map<String, AbstractPropertyValue<?>> map = new HashMap<String, AbstractPropertyValue<?>>();
        for (Collection<? extends AbstractPropertyValue<?>> values : this.valuesToBeMerged) {
            this.merge(map, values);
        }
        return map;
    }

    /**
     * @param map a map from properties' mnemonic to the corresponding merged {@link AbstractPropertyValue}.
     * @param values the values to be merged onto this map
     */
    @SuppressWarnings("unchecked")
    private void merge(Map<String, AbstractPropertyValue<?>> map, Collection<? extends AbstractPropertyValue<?>> values) {
        for (AbstractPropertyValue value : values) {
            String key = value.getProperty().getMnemonic();
            map.put(key, map.containsKey(key) ? map.remove(key).merge(value) : value);
        }
    }

    /**
     * Resets the state of this {@link PropertyValueMerger}.
     */
    public void clear() {
        this.valuesToBeMerged.clear();
    }

}
